package com.thinkfit.jy.utils;

import com.common.lib.constant.EventBusEvent;
import com.common.lib.network.HttpMethods;
import com.common.lib.utils.LogUtil;
import com.thinkfit.jy.serialport.DeviceType;
import com.thinkfit.jy.serialport.SerialPort;
import com.thinkfit.jy.serialport.SerialPortManager;
import com.thinkfit.jy.serialport.SportCalculateManager;
import com.thinkfit.jy.serialport.SportStatus;

import org.greenrobot.eventbus.EventBus;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;

import io.reactivex.Observable;
import io.reactivex.Observer;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;

public class SerialPortUtils {

    private static final String TAG  = "ddddddddddddddddddddddd  SerialPortUtils ------------->";

    static SerialPortUtils serialPortUtils;

    private FileInputStream mFileInputStream = null;
    private FileOutputStream mFileOutputStream = null;
    private SerialPort mSerialPort = null;
    public static DeviceType mCurrentDeviceType = DeviceType.CAR_WATCH;
    private final int UART_SERVICE_STATUS_VERIFY = 0;
    private final int UART_SERVICE_STATUS_NORMAL = 1;
    private int mStatus = UART_SERVICE_STATUS_VERIFY;

    private SportStatus mRunStatus = SportStatus.STOP;

    Disposable equipmentStateDisposable,sportsInfoDisposable,connectDetectionDisposable,
            bluetoothOrderWriteDisposable,serialReturnDisposable;
    private static final int GET_EQUIPMENT_COUNT_DOWN = 260;
    private static final int GET_SPORTS_COUNT_DOWN = 460;
    private static final int OTHER_COUNT_DOWN = 300;
    private int counter = 0;
    private long mStartTime;
    private int repetitionNum = 3;
    private static final int LOOP_NUM = 3;
    private SerialPortManager.CurrentInfo mCurrentInfo;

    public static SerialPortUtils getInstance(){
        if (serialPortUtils == null) {
            synchronized (TAG) {
                if(serialPortUtils == null){
                    serialPortUtils = new SerialPortUtils();
                }
            }
        }
        return serialPortUtils;
    }

    public SerialPortUtils(){
        connectDetection();//定时获取连接状态
        serialReturn();//持续获取返回数据
        initUart(9600);
    }


    private boolean initUart(int baudrate) {
        try {
            mSerialPort = new SerialPort(new File("/dev/ttyMcuUart"), baudrate, 0);
        } catch (IOException e) {
            e.printStackTrace();
            LogUtil.LogE(e.toString());
            return false;
        }
        mFileInputStream = mSerialPort.getInputStream();
        mFileOutputStream = mSerialPort.getOutputStream();

        return true;
    }

    /**
     * 获取FCS（转换FCS）
     * @param buf
     * @param len
     * @return
     */
    public byte UartFCS(byte[] buf, int len) {
        byte i = 0, fcs = 0;
        for (i = 1; i < len; i++) {
            fcs ^= buf[i];
        }
        return fcs;
    }

    private void verify() {
        byte[] buf = new byte[32];
        int length = 0;
        buf[0] = 0x02;
        buf[1] = 0x50;
        buf[2] = 0x00;
        buf[3] = 0x50;
        buf[4] = 0x03;
        switch (counter) {
            case 0:
                if (initUart(4800)) {
                    System.out.println("ddddddddddddddddddddddd  4800 ");
                    try {
                        mFileOutputStream.write(buf, 0, 5);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                break;
            case 10:
                if (initUart(9600)) {
                    System.out.println("ddddddddddddddddddddddd  9600 ");
                    try {
                        mFileOutputStream.write(buf, 0, 5);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                break;
            case 20:
                if (initUart(19200)) {
                    System.out.println("ddddddddddddddddddddddd  19200 ");
                    try {
                        mFileOutputStream.write(buf, 0, 5);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                break;
            case 30:
                if (initUart(38400)) {
                    System.out.println("ddddddddddddddddddddddd  38400 ");
                    try {
                        mFileOutputStream.write(buf, 0, 5);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                break;
            case 40:
                if (initUart(115200)) {
                    System.out.println("ddddddddddddddddddddddd  115200 ");
                    try {
                        mFileOutputStream.write(buf, 0, 5);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                break;
        }
        if (++counter >= 50) {
            counter = 0;
        }
        try {
            length = mFileInputStream.read(buf, 0, 32);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if(length > 0 && length < 32){
            byte[] tmp_buf;
            LogUtil.LogE(length);
            if (length >= 4) {
                String response = bytesToHex(buf) + "";
                byte[] enen = Arrays.copyOfRange(buf, 0, length);
                System.out.println("ddddddddddddddddddddddd  enen : "+ bytesToHex(enen) + "");
                System.out.println("ddddddddddddddddddddddd  length : "+ length +"   response : "+response);
                tmp_buf = new byte[length-2];
                System.arraycopy(enen, 0, tmp_buf, 0, length - 2);
                System.out.println("ddddddddddddddddddddddd  是否成功连接串口 : "+(UartFCS(tmp_buf, tmp_buf.length-1) == enen[length - 2]));

                if (UartFCS(tmp_buf, tmp_buf.length-1) == enen[length - 2]) {
                    mStatus = UART_SERVICE_STATUS_NORMAL;

                    DeviceType type = mCurrentDeviceType;
                    //       if (response.startsWith("025000")) {
                    initInput(enen);

                    if (type != mCurrentDeviceType) {
                        HttpMethods.Companion.getInstance().setSType(mCurrentDeviceType == DeviceType.CAR_WATCH ?
                                "1" : "2");
                        HashMap<String, Object> map = new HashMap<>();
                        map.put(EventBusEvent.RESTART_APP, "");
                        EventBus.getDefault().post(map);
                    }
//                    if (mCurrentDeviceType == DeviceType.RUNNING_MACHINE) {  //跑步机才调用
//                        addCmd(getSpeedCmd());
//                        addCmd(getSlopCmd());
//                    }
//                        else {
//                            getDeviceStatus();
//                        }
                }
            }
        }
    }

    private void normal() {
        byte[] buf = new byte[32];
        int length = 0;
        try {
            length = mFileInputStream.read(buf, 0, 32);
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("ddddddddddddddddddddddd   normal   length: " + length);
        if (length > 0 && length < 32) {
            byte[] tmp_buf = new byte[length];
            System.arraycopy(buf, 0, tmp_buf, 0, length);
//            String response = bytesToHex(buf) + "";
            System.out.println("ddddddddddddddddddddddd    normal   tmp_buf: " + bytesToHex(tmp_buf));
            initInput(tmp_buf);
        }
    }

    public void initInput(byte[] enen){
        String instruct1 = Long.toString(enen[1] & 0xff, 16);
        String instruct2 = Long.toString(enen[2] & 0xff, 16);//转换会把前面的"0"给略掉，需要可以用StringBuffer拼上
        if(enen == null && enen.length <= 0 ){
            return;
        }
        if(instruct1.equals("41")&& instruct2.equals("2")){
            //设备参数
            BluetoothOrder.getInstance().equipmentInfo(enen);

        }else if(instruct1.equals("50")&& instruct2.equals("0")){
            //获取设备品牌
            BluetoothOrder.getInstance().getBrandInfo(enen);

        }else if(instruct1.equals("42")){
            //获取设备状态
            BluetoothOrder.getInstance().getEquipmentState(enen);

        }else if(instruct1.equals("43")&&instruct2.equals("1")){
            //获取运动数据
            BluetoothOrder.getInstance().getSportsData(enen);
        }else if(instruct1.equals("44")&&instruct2.equals("1")){
            //准备就绪
            System.out.println("ddddddddddddddddddddddd   蓝牙通知界面准备就绪状态  ");
            if(mRunStatus == SportStatus.START){
                return;
            }
            mRunStatus = SportStatus.START;
            int countdown = BluetoothOrder.getInstance().formatHexInt(Long.toString(enen[3] & 0xff, 16));
            System.out.println(TAG+" countdown : "+countdown);
            if (countdown > 0) {
                HashMap<String, Object> map = new HashMap<>();
                map.put(EventBusEvent.START_RUN, countdown);
                EventBus.getDefault().post(map);
            }else {
                sendMsg(BluetoothOrder.EQUIPMENT_START_OR_CONTINUE);
            }
        }else if(instruct1.equals("44")&&instruct2.equals("2")){
            //设备开始或继续
            System.out.println("ddddddddddddddddddddddd   蓝牙通知界面刷新开始状态  "+(mRunStatus == SportStatus.RUNNING));
            if(mRunStatus == SportStatus.RUNNING){
                return;
            }
            mRunStatus = SportStatus.RUNNING;
            getEquipmentState();
//                        BluetoothOrder.getInstance().getEquipmentState();//获取设备状态
            Observable.timer(500, TimeUnit.MILLISECONDS)
                    .subscribe(new Consumer<Long>() {
                        @Override
                        public void accept(Long aLong) throws Exception {
                            getSportsInfo();//获取运动数据
                        }
                    });

            System.out.println("ddddddddddddddddddddddd   蓝牙通知界面刷新开始状态");
            HashMap<String, Object> map = new HashMap<>();
            map.put(EventBusEvent.RUNNING, 0);
            EventBus.getDefault().post(map);

        }else if(instruct1.equals("44")&&instruct2.equals("3")){
            //设备暂停
            if(mRunStatus == SportStatus.PAUSE){
                return;
            }
            mRunStatus = SportStatus.PAUSE;
            System.out.println("ddddddddddddddddddddddd   蓝牙通知界面刷新暂停状态");
            HashMap<String, Object> map = new HashMap<>();
            map.put(EventBusEvent.PAUSE_RUN, 0);
            EventBus.getDefault().post(map);
        }else if(instruct1.equals("44")&&instruct2.equals("4")){
            //设备停止
            if(mRunStatus == SportStatus.STOP){
                return;
            }
            mRunStatus = SportStatus.STOP;
            System.out.println("ddddddddddddddddddddddd   蓝牙通知界面刷新停止状态");
            HashMap<String, Object> map = new HashMap<>();
            map.put(EventBusEvent.STOP_RUN, 0);
            EventBus.getDefault().post(map);
        }else if(instruct1.equals("44")&&instruct2.equals("5")){
            //设置阻力和坡度

        }else if(instruct1.equals("50")&&instruct2.equals("2")){
            //获取跑步机速度参数
            BluetoothOrder.getInstance().setTreadmillSpeed(enen);

        }else if(instruct1.equals("50")&&instruct2.equals("3")){
            //获取跑步机坡度参数
            BluetoothOrder.getInstance().setTreadmillSlop(enen);

        }else if(instruct1.equals("50")&&instruct2.equals("4")){
            //获取跑步机累计⾥程
            BluetoothOrder.getInstance().setTreadmillMileage(enen);

        }else if(instruct1.equals("51")){
            //获取跑步机状态
            BluetoothOrder.getInstance().getTreadmillInfo(enen);

        }else if(instruct1.equals("53")&&instruct2.equals("1")){
            //跑步机准备就绪
            System.out.println("ddddddddddddddddddddddd   蓝牙通知跑步机准备就绪状态  ");
            if(mRunStatus == SportStatus.START){
                return;
            }
            mRunStatus = SportStatus.START;
            int countdown = BluetoothOrder.getInstance().formatHexInt(Long.toString(enen[3] & 0xff, 16));
            System.out.println(TAG+" countdown : "+countdown);
            if (countdown > 0) {
                HashMap<String, Object> map = new HashMap<>();
                map.put(EventBusEvent.START_RUN, countdown);
                EventBus.getDefault().post(map);
            }else {
                HashMap<String, Object> map = new HashMap<>();
                map.put(EventBusEvent.RUNNING, 0);
                EventBus.getDefault().post(map);

//                            BluetoothOrder.getInstance().getTreadmillState();
            }

        }else if(instruct1.equals("53")&&instruct2.equals("2")){
            //跑步机设置速度和坡度指令
            BluetoothOrder.getInstance().treadmillSpeedAndSlope(enen);

        }else if(instruct1.equals("53")&&instruct2.equals("3")){
            //跑步机停止指令
            if(mRunStatus == SportStatus.STOP){
                return;
            }
            mRunStatus = SportStatus.STOP;
            System.out.println("ddddddddddddddddddddddd   蓝牙通知界面刷新停止状态");
            HashMap<String, Object> map = new HashMap<>();
            map.put(EventBusEvent.STOP_RUN, 0);
            EventBus.getDefault().post(map);
        }else if(instruct1.equals("53")&&instruct2.equals("9")){
            //跑步机开始指令
            System.out.println("ddddddddddddddddddddddd   蓝牙通知跑步机刷新开始状态  "+(mRunStatus == SportStatus.RUNNING));
            if(mRunStatus == SportStatus.RUNNING){
                return;
            }
            mRunStatus = SportStatus.RUNNING;


            System.out.println("ddddddddddddddddddddddd   蓝牙通知界面刷新开始状态");
            HashMap<String, Object> map = new HashMap<>();
            map.put(EventBusEvent.RUNNING, 0);
            EventBus.getDefault().post(map);

        }else if(instruct1.equals("53")&&instruct2.equals("A")){
            //跑步机暂停指令
            if(mRunStatus == SportStatus.PAUSE){
                return;
            }
            mRunStatus = SportStatus.PAUSE;
            System.out.println("ddddddddddddddddddddddd   蓝牙通知界面刷新暂停状态");
            HashMap<String, Object> map = new HashMap<>();
            map.put(EventBusEvent.PAUSE_RUN, 0);
            EventBus.getDefault().post(map);
        }
    }

    public void startRun() {
        SerialPortManager.Cmd cmd = new SerialPortManager.Cmd();
        cmd.cmd = 3;
        if (mCurrentDeviceType == DeviceType.RUNNING_MACHINE) {
            System.out.println("ddddddddddddddddddddddd  RUNNING_MACHINE ");
//            cmd.bytes = new byte[]{0x02, 0x53, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
//                    handData("53010000000000000000"), 0x03};
            cmd.bytes = new byte[]{0x02,0x53,0x01,0x50,0x03};
            mRunStatus = SportStatus.START;
            mStartTime = System.currentTimeMillis();
            HashMap<String, Object> map = new HashMap<>();
            map.put(EventBusEvent.START_RUN, 0);
            EventBus.getDefault().post(map);
        } else if (mCurrentDeviceType == DeviceType.CAR_WATCH) {
            System.out.println("ddddddddddddddddddddddd  CAR_WATCH ");
            cmd.bytes = new byte[]{0x02, 0x44, 0x01, 0x45, 0x03};
//            cmd.bytes = new byte[]{0x02, 0x07, 0x61, 0x39, 0x01, 0x00, 0x00, 0x00, 0x00, handData("0761390100000000"), 0x03};
            mRunStatus = SportStatus.START;
            mStartTime = System.currentTimeMillis();
            HashMap<String, Object> map = new HashMap<>();
            map.put(EventBusEvent.START_RUN, 0);
            EventBus.getDefault().post(map);
        }
//        addCmd(cmd);
        sendMsg(cmd.bytes);
    }
    public void stopRun() {
        SerialPortManager.Cmd cmd = new SerialPortManager.Cmd();
        cmd.cmd = 4;
        if (mCurrentDeviceType == DeviceType.RUNNING_MACHINE) {
            cmd.bytes = new byte[]{0x02, 0x53, 0x03, handData("5303"), 0x03};
        } else if (mCurrentDeviceType == DeviceType.CAR_WATCH) {
            cmd.bytes = new byte[]{0x02, 0x44, 0x04, 0x40, 0x03};
//            cmd.bytes = new byte[]{0x02, 0x07, 0x61, 0x39, 0x01, 0x00, 0x00, 0x00, 0x00, handData("0761390100000000"), 0x03};
            mRunStatus = SportStatus.STOP;
            mStartTime = 0;
            HashMap<String, Object> map = new HashMap<>();
            map.put(EventBusEvent.STOP_RUN, 0);
            EventBus.getDefault().post(map);

            getCurrentInfo().lastCount = 0;
            getCurrentInfo().count = 0;
            getCurrentInfo().time = 0;
            getCurrentInfo().rpm = 0;
            getCurrentInfo().resistance = 0;
            SportCalculateManager.getInstance().resetSportData();
        }
//        addCmd(cmd);
        sendMsg(cmd.bytes);
    }

    public void setSpeedSlop(int speed, int slop) {
//        SerialPortManager.Cmd cmd = new SerialPortManager.Cmd();
//        cmd.cmd = 5;
        byte[] buy = new byte[]{0x02, 0x53, 0x02, (byte) speed, (byte) slop,
                handData("5302" + getNewHex(speed) + getNewHex(slop)), 0x03};
        sendMsg(buy);
    }

    public void setResistanceSlop(int resistance, int slop) {
        SerialPortManager.Cmd cmd = new SerialPortManager.Cmd();
        cmd.cmd = 5;
        byte[] buy = BluetoothOrder.getInstance().getNowelAndSlopeInstruct(resistance,slop);
        sendMsg(buy);
    }

    public void connectDetection(){
        closeConnectDetection();
        Observable.interval(2000, TimeUnit.MILLISECONDS)
                .subscribe(new Observer<Long>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        connectDetectionDisposable = d;
                    }

                    @Override
                    public void onNext(Long aLong) {
                        verify();
//                        sendMsg(BluetoothOrder.EQUIPMENT_GET_EQUIPMENT_INFO);

                    }

                    @Override
                    public void onError(Throwable e) {
                        closeConnectDetection();
                    }

                    @Override
                    public void onComplete() {
                        closeConnectDetection();
                    }
                });
    }

    public void serialReturn(){
        closeSerialReturnDisposable();
        Observable.interval(200, TimeUnit.MILLISECONDS)
                .subscribe(new io.reactivex.Observer<Long>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        serialReturnDisposable = d;
                    }

                    @Override
                    public void onNext(Long aLong) {
                        normal();
//                        sendMsg(BluetoothOrder.EQUIPMENT_GET_EQUIPMENT_INFO);
                    }

                    @Override
                    public void onError(Throwable e) {
                        closeSerialReturnDisposable();
                    }

                    @Override
                    public void onComplete() {
                        closeSerialReturnDisposable();
                    }
                });
    }

    public void getEquipmentState(){
        if(equipmentStateDisposable != null){
            equipmentStateDisposable.dispose();
            equipmentStateDisposable = null;
        }
        Observable.interval(GET_EQUIPMENT_COUNT_DOWN, TimeUnit.MILLISECONDS)
                .subscribe(new io.reactivex.Observer<Long>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        equipmentStateDisposable = d;
                    }

                    @Override
                    public void onNext(Long aLong) {
                        sendMsg(BluetoothOrder.EQUIPMENT_GET_EQUIPMENT_INFO);
                    }

                    @Override
                    public void onError(Throwable e) {
                        closeRxJavaClone();
                    }

                    @Override
                    public void onComplete() {
                        closeRxJavaClone();
                    }
                });
    }

    public void getSportsInfo(){
        if(sportsInfoDisposable != null){
            sportsInfoDisposable.dispose();
            sportsInfoDisposable = null;
        }
        Observable.interval(GET_SPORTS_COUNT_DOWN, TimeUnit.MILLISECONDS)
                .subscribe(new io.reactivex.Observer<Long>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        sportsInfoDisposable = d;
                    }

                    @Override
                    public void onNext(Long aLong) {
                        sendMsg(BluetoothOrder.EQUIPMENT_GET_SPORTS_INFO);
                    }

                    @Override
                    public void onError(Throwable e) {
                        closeRxJavaClone();
                    }

                    @Override
                    public void onComplete() {
                        closeRxJavaClone();
                    }
                });
    }

    public void closeRxJavaClone(){
        if(equipmentStateDisposable != null){
            equipmentStateDisposable.dispose();
            equipmentStateDisposable = null;
        }
        if(sportsInfoDisposable != null){
            sportsInfoDisposable.dispose();
            sportsInfoDisposable = null;
        }
    }

    public void closeConnectDetection(){
        if(connectDetectionDisposable != null){
            connectDetectionDisposable.dispose();
            connectDetectionDisposable = null;
        }

    }
    public void closeSerialReturnDisposable(){
        if(serialReturnDisposable != null){
            serialReturnDisposable.dispose();
            serialReturnDisposable = null;
        }

    }

    /**
     * 命令写入串口
     * @param buf
     */
    public void sendMsgOther(byte[] buf) {
        System.out.println("ddddddddddddddddddddddd    sendMsg :  "+bytesToHex(buf));
        closeBluetoothOrderWrite();
        Observable.interval(OTHER_COUNT_DOWN, TimeUnit.MILLISECONDS)
                .subscribe(new Observer<Long>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        bluetoothOrderWriteDisposable = d;
                    }

                    @Override
                    public void onNext(Long aLong) {
                        if(aLong >= LOOP_NUM){
                            closeBluetoothOrderWrite();
                        }
                        sendMsg(buf);
                    }

                    @Override
                    public void onError(Throwable e) {
                        closeBluetoothOrderWrite();
                    }

                    @Override
                    public void onComplete() {
                        closeBluetoothOrderWrite();
                    }
                });

    }
    public void sendMsg(byte[] buf){
        System.out.println("ddddddddddddddddddddddd    sendMsg :  "+bytesToHex(buf));
        if (isConnected() && mFileOutputStream != null) {
            try {
                mFileOutputStream.write(buf);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public void closeBluetoothOrderWrite(){
        if(bluetoothOrderWriteDisposable != null){
            bluetoothOrderWriteDisposable.dispose();
            bluetoothOrderWriteDisposable = null;
        }
    }

    public SerialPortManager.CurrentInfo getCurrentInfo() {
        if (mCurrentInfo == null) {
            mCurrentInfo = new SerialPortManager.CurrentInfo();
        }
        return mCurrentInfo;
    }

    private byte handData(String text) {   //异或处理
        int length = text.length();
        int result = Integer.parseInt(text.substring(0, 2), 16);
        for (int i = 2; i < length; i = i + 2) {
            result = result ^ Integer.parseInt(text.substring(i, i + 2), 16);
        }
        return (byte) result;
    }

    private String getNewHex(int value) {
        String hex = Integer.toHexString(value);
        if (hex.length() == 1) {
            return "0" + hex;
        }
        return hex;
    }

    public boolean isConnected() {
        return mStatus == UART_SERVICE_STATUS_NORMAL;
    }

    public boolean isRun() {
        return mRunStatus == SportStatus.START || mRunStatus == SportStatus.RUNNING || mRunStatus == SportStatus.PAUSE;
    }

    public void closeCurrentInfo(){
        if (mCurrentInfo != null){
            mCurrentInfo = null;
        }
    }

    private String bytesToHex(byte[] bytes) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < bytes.length; i++) {
            String hex = Integer.toHexString(bytes[i] & 0xFF);
            if (hex.length() < 2) {
                sb.append(0);
            }
            sb.append(hex);
        }
        return sb.toString();
    }

}
